//
//  LEDBConfig.m
//  CreditAddressBook
//
//  Created by ox on 15/10/27.
//  Copyright © 2015年 Lee. All rights reserved.
//

#import "LEDBConfig.h"

@interface LEDBConfig ()

@property(nonatomic, strong) NSString *dbPath;
//@property(nonatomic, assign) NSInteger newVersion;
//@property(nonatomic, assign) NSInteger oldVersion;
@property(nonatomic, strong) NSString *tableName;
@property(nonatomic, strong) NSString *sql;
@end

@implementation LEDBConfig

- (NSString *)databaseName {
    return @"database.db";
}

- (void)createTables {}

- (void)prepareDatabase {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //手动创建数据库文件并复制到指定路径
        //        NSFileManager *fm = [NSFileManager defaultManager];
        //        if (![fm fileExistsAtPath:[self dbPath]]) {
        //            NSURL *prototypeDBURL = [[NSBundle mainBundle] URLForResource:@"by_call_history" withExtension:@"sqlite"];
        //            NSError *error = nil;
        //            [fm copyItemAtURL:prototypeDBURL toURL:[NSURL fileURLWithPath:[self dbPath]] error:&error];
        //            if (error) {
        //                CYLog(@"%@", error);
        //            }
        //        }
        
        _dbQueue = [FMDatabaseQueue databaseQueueWithPath:self.dbPath];
//        NSLog(@"dbPath: %@", self.dbPath);
        
        //建表
        [self createTables];
    });
}

- (NSString *)dbPath {
    if (!_dbPath) {
        //获取沙盒中数据库的名称
        _dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[self databaseName]];
    }
    return _dbPath;
}

- (BOOL)createTable:(NSString *)sql tableName:(NSString *)tableName version:(NSInteger)newVersion {
//    self.newVersion = newVersion;
    self.tableName = tableName;
    self.sql = sql;
    
    __block BOOL result = NO;
    [_dbQueue inDatabase:^(FMDatabase *db) {
        result = [db executeUpdate:@"create table if not exists t_version(id text primary key UNIQUE not null, version integer);"];
        if (result) {
            result = [self checkDatabase:db newVersion:newVersion];
            if (!result) {
                NSAssert(0, @"数据库版本检查失败");
            }
        } else {
            NSAssert(0, @"version表创建失败");
        }
    }];
    return result;
}

/**
 * 检查数据库（初始化数据库或更新数据库）
 */
- (BOOL)checkDatabase:(FMDatabase *)db newVersion:(NSInteger)newVersion {
    if (!db) {
        NSAssert1(0, @"db path and name can't be empty (%@)", db.databasePath);
        @throw [NSException exceptionWithName:@"Database name error" reason:@"Database name and path can not be empty." userInfo:nil];
        return NO;
    }
    
    @try {
        if (![db open]) {
            //NSLog(@"db open fail");
            NSAssert1(0, @"db 打开失败 (%@)", db.databasePath);
            return NO;
        }
        
        //查出当前数据库版本
        NSInteger oldVersion = -1;
        FMResultSet *rs = [db executeQuery:@"select version from t_version where id = ?", self.tableName];
        if ([rs next]) {
            oldVersion = [rs intForColumn:@"version"];
//            self.oldVersion = oldVersion;
        }
        [rs close];
        
        if (oldVersion <= 0) { //表示第一次创建数据库
            [db beginTransaction];
            BOOL rev = [self onCreate:db newVersion:newVersion oldVersion:oldVersion];
            if (rev) {
                rev = [db executeUpdate:@"insert into t_version (id, version) values(?, ?);", self.tableName, @(newVersion)];
                if (rev)
                    [db commit];
                else {
                    CYLog(@">>>>>>>>>>>> db exec fail: %@", [db lastError]);
                    [db rollback];
                }
            } else {
                [db rollback];
                CYLog(@">>>>>>>>>>>> db exec fail: %@", [db lastError]);
            }
            [self didChecked:db dbCheckIsSuccess:rev];
            return rev;
        } else { //表示已经创建了库表，接下来走onUpgrade等，由开发者在子类中决定如何升级或降级库表结构
            
            if (newVersion < oldVersion) { //新版本号小于旧版本号
                CYLog(@"数据库降级");
//                if (self.allowDowngrade) {
//                    //执行用户的降级代码
//                    [db beginTransaction];
//                    BOOL rev = [self onDowngrade:self.db oldVersion:oldVersion newVersion:newVersion];
//                    if (rev) {
//                        rev = [db executeUpdate:[NSString stringWithFormat:@"PRAGMA user_version = %d", newVersion]];
//                        if (rev)
//                            [db commit];
//                        else {
//                            [db rollback];
//                            NSLog(@">>> db exec fail: %@", [db lastError]);
//                        }
//                    } else {
//                        [db rollback];
//                        NSLog(@">>> db exec fail: %@", [db lastError]);
//                    }
//                    //
//                    [self didChecked:db dbCheckIsSuccess:rev];
//                    return rev;
//                } else { //禁止降级
//                    NSString *errStr = [NSString stringWithFormat:@"The database new version(%d) cannot be less than the old version(%d)", newVersion, oldVersion];
//                    NSAssert2(0, errStr, newVersion, oldVersion);
//                    //                    @throw [NSException exceptionWithName:@"Database version error" reason:errStr userInfo:nil];
//                    //
//                    [self didChecked:db dbCheckIsSuccess:NO];
//                    return NO;
//                }
            } else if (newVersion == oldVersion) { //新旧版本号相同
                //执行用户的代码
                [db beginTransaction];
                BOOL rev = [self onEqual:db oldVersion:oldVersion newVersion:newVersion];
                if (rev)
                    [db commit];
                else {
                    [db rollback];
                    CYLog(@">>>>>>>>>>>> db exec fail: %@", [db lastError]);
                }
                //
                [self didChecked:db dbCheckIsSuccess:rev];
                return rev;
            } else { //新版本号大于旧版本号则执行onUpgrade里的方法
                //执行用户的更新代码
                [db beginTransaction];
                BOOL rev = [self onUpgrade:db oldVersion:oldVersion newVersion:newVersion];
                if (rev) {
                    rev = [db executeUpdate:@"update t_version set version = ? where id = ?", @(newVersion), self.tableName];
                    if (rev)
                        [db commit];
                    else {
                        [db rollback];
                        CYLog(@">>>>>>>>>>>> db exec fail: %@", [db lastError]);
                    }
                } else {
                    [db rollback];
                    CYLog(@">>>>>>>>>>>> db exec fail: %@", [db lastError]);
                }
                //
                [self didChecked:db dbCheckIsSuccess:rev];
                return rev;
            }
        }
    } @catch (NSException *ex) {
        [self reportException:ex reason:@"初始化数据库或更新数据库错误"];
        NSAssert1(0, @"Exception: %@", ex.reason);
        return NO;
    } @finally {
        //        [self.db close];
    }
    return NO;
}

/**
 * 第一次创建数据库时的方法
 *
 * @param db FMDB的数据库对象
 * @return YES=成功，NO=失败
 */
- (BOOL)onCreate:(FMDatabase *)db newVersion:(NSInteger)newVersion oldVersion:(NSInteger)oldVersion {
    if (!db) {
        NSAssert(0, @"db不能为空");
        return false;
    }
    
    @try {
        BOOL result = NO;
        FMResultSet *rs = [db executeQuery:@"SELECT sql FROM sqlite_master WHERE type = 'table' AND tbl_name = ?;", self.tableName];
        if (rs.next) {
            return [self onUpgrade:db oldVersion:oldVersion newVersion:newVersion];
        }
        if (self.sql != nil) {
            result = [db executeUpdate:self.sql];
        }
        if (!result) {
            CYLog(@"创建table失败");
            return false;
        }
        return result;
    } @catch (NSException *exception) {
        NSAssert1(0, @"Exception: %@", exception.reason);
        return false;
    } @finally {
        
    }
}


//- (BOOL)onUpgradeWithAddFields:(NSDictionary *)fields deleteFields:(NSArray *)dels {
//    if (!db) {
//        NSAssert(0, @"db can't be null");
//        return false;
//    }
//    
//    @try {
//        BOOL rev = NO;
//        for(int ver = oldVersion; ver < newVersion; ver++) {
//            rev = NO;
//            switch(ver) {
//                case 2:
//                    rev = [self upgradeVersion1To2:db];
//                    break ;
//                    
//                default :
//                    break ;
//            }
//            if (!rev) return false;
//        }
//        
//        return true;
//    }
//    @catch (NSException *exception) {
//        NSAssert1(0, @"Exception: %@", exception.reason);
//        return false;
//    }
//    @finally {
//        
//    }
//}

/**
 * 数据库版本相等时的方法。子类可以覆盖该方法，实现数据库版本相等时的代码
 *
 * @param db FMDB的数据库对象
 * @param oldVersion 当期数据库的版本
 * @param newVersion 要更新的新的数据库的版本
 * @return YES=成功，NO=失败
 */
- (BOOL)onEqual:(FMDatabase *)db oldVersion:(NSInteger)oldVersion newVersion:(NSInteger)newVersion {
    return YES;
}

/**
 * 数据库版本增加时的方法。
 *
 * @param db FMDB的数据库对象
 * @param oldVersion 当期数据库的版本
 * @param newVersion 要更新的新的数据库的版本
 * @return YES=成功，NO=失败
 */
- (BOOL)onUpgrade:(FMDatabase *)db oldVersion:(NSInteger)oldVersion newVersion:(NSInteger)newVersion {
    @try {
        //获取表字段名称与类型
        FMResultSet *rs = [db executeQuery:[NSString stringWithFormat:@"pragma table_info('%@')", self.tableName]];
        NSMutableDictionary *oldTable = [NSMutableDictionary dictionary];
        while (rs.next) {
            oldTable[[rs stringForColumn:@"name"]] = [rs stringForColumn:@"type"];
        }
        
        //删除同名临时表
        [db executeUpdate:[NSString stringWithFormat:@"DROP TABLE %@_%ld;", self.tableName, (long)oldVersion]];
        //创建临时表
        BOOL result = [db executeUpdate:[NSString stringWithFormat:@"ALTER TABLE '%@' RENAME TO '%@_%ld'", self.tableName, self.tableName, (long)oldVersion]];
        if (result) {
            //创建新表
            [db executeUpdate:self.sql];
        } else {
            NSAssert(0, @"创建临时表失败");
            return NO;
        }
        
        FMResultSet *rsn = [db executeQuery:[NSString stringWithFormat:@"pragma table_info('%@')", self.tableName]];
        NSMutableDictionary *newTable = [NSMutableDictionary dictionary];
        while (rsn.next) {
            newTable[[rsn stringForColumn:@"name"]] = [rsn stringForColumn:@"type"];
        }
        
        //迁移数据到新表
        NSMutableArray *keys = [NSMutableArray array];
        NSMutableString *sm = [NSMutableString string];
        for (NSString *key in [oldTable allKeys]) {
            if (newTable[key]) {
                [keys addObject:key];
                [sm appendFormat:@"'%@', ", key];
            }
        }
        NSString *keySm = [sm substringToIndex:sm.length - 2];
        
        result = [db executeUpdate:[NSString stringWithFormat:@"INSERT INTO '%@' (%@) SELECT %@ FROM '%@_%ld'", self.tableName, keySm, keySm, self.tableName, (long)oldVersion]];
        if (!result) {
            NSAssert(0, @"数据迁移失败");
            return NO;
        }
        //删除临时表
        //    [db executeUpdate:@"DROP TABLE %@_%@", self.tableName, self.newVersion];
    }
    @catch (NSException *exception) {
        NSAssert1(1, @"表升级失败:%@", exception);
        return NO;
    }
    @finally {
        
    }
    return YES;
}

/**
 * 数据库版本降级时的方法。子类可以覆盖该方法，实现数据库版本降级时的代码
 *
 * @param db FMDB的数据库对象
 * @param oldVersion 当期数据库的版本
 * @param newVersion 要更新的新的数据库的版本
 * @return YES=成功，NO=失败
 */
- (BOOL)onDowngrade:(FMDatabase *)db oldVersion:(int)oldVersion newVersion:(int)newVersion {
    return YES;
}

/**
 * 数据库配置检查完成后会调用的方法。子类可以覆盖该方法，实现数据库版本升级后的一些后续数据处理。
 *
 * @param db FMDB的数据库对象
 * @param dbCheckIsSuccess 数据库配置检查是否成功了
 */
- (void)didChecked:(FMDatabase *)db dbCheckIsSuccess:(BOOL)dbCheckIsSuccess {
    
}
@end
